package au.edu.qut.bpm.genbinder.genetic.other;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import org.uncommons.watchmaker.framework.EvolutionaryOperator;

import au.edu.qut.bpm.genbinder.models.ConcreteEventLog;

public class CopyOfLogRepairer implements EvolutionaryOperator<ConcreteEventLog> {

	public CopyOfLogRepairer() {

	}

	public List<ConcreteEventLog> apply(List<ConcreteEventLog> selectedCandidates, Random rng) {
		List<ConcreteEventLog> fixed = new ArrayList<ConcreteEventLog>(selectedCandidates.size());
		final CopyOfLogRepairerSingleLog repair = new CopyOfLogRepairerSingleLog();

		for (ConcreteEventLog log : selectedCandidates) {
			ConcreteEventLog fixedLog = log.clone();
			//System.out.println("fixedLog-" + fixedLog.getAbstractLog().getActivities());
			//if (!this.performTimeRepair(fixedLog) || !this.performResourceRepair(fixedLog))
				//fixedLog = this.performFallbackRepair(fixedLog);
			try {
				fixed.add(repair.RepairOneLog(fixedLog));
				//log.RepairOneLog();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
		return fixed;
	}
	/*
	public boolean performTimeRepair(ConcreteEventLog log) {
		// Time based repairs
		for (Case c : log.getAbstractLog().getCases()) {
			if (log.isCaseOrdered(c)) {
				// This case is ordered, but are there overlaps?
				if (log.isCaseNonOverlapping(c))
					continue;
				List<Activity> sortedTrace = log.getSortedTrace(c);
				long latestEnd = log.getActivityEnd(sortedTrace.get(0));
				for (int a = 1; a < sortedTrace.size(); a++) {
					if (log.getActivityStart(sortedTrace.get(a)) < latestEnd)
						log.setActivityStart(sortedTrace.get(a), latestEnd);
					latestEnd = log.getActivityEnd(sortedTrace.get(a));
				}
			} else {
				// This case is unordered, let's fix it
				List<Activity> newOrder = c.getOrderedTrace();
				for (int a = 0; a < newOrder.size(); a++) {
					Activity act = newOrder.get(a);
					long startTime = a == 0
							? c.getArrival()
							: log.getActivityEnd(act);
					log.setActivityStart(act, startTime);
				}
			}
		}
		return true;
	}

	public boolean performResourceRepair(ConcreteEventLog log) {
		// Resource based repairs
		Random rng = new Random(); // Meh...
		for (Case c : log.getAbstractLog().getCases()) {
			if (log.isCaseNonOverUtilizedResources(c))
				continue;
			for (Activity a : c.getActivities()) {
				Set<Set<Resource>> possible = log.getAbstractLog().getTaskResourceSetMap().get(a.getTask());
				Set<Set<Resource>> available = new HashSet<Set<Resource>>(possible);
				Set<Resource> alreadyUsed = log.getResourcesUtilizedWithin(log.getActivityStart(a), log.getActivityEnd(a), c);
				for (Resource r : alreadyUsed)
					for (Set<Resource> rs : possible)
						if (rs.contains(r))
							available.remove(rs);
				if (available.size() == 0) {
					return false;
				} else {
					int item = rng.nextInt(available.size());
					int i = 0;
					for (Set<Resource> r : available) {
						if (i == item) {
							log.setActivityResources(a, r);
							break;
						}
						i++;
					}
				}
			}
		}
		return true;
	}

	public ConcreteEventLog performFallbackRepair(ConcreteEventLog fixedLog) {
		LogGreedyFactory factory = new LogGreedyFactory(fixedLog.getAbstractLog());
		fixedLog = factory.generateRandomCandidate(new Random());

		if (!fixedLog.isLogSafe())
			System.err.println("Solution was still not safe after last resort repair!");

		return fixedLog;
	}//*/
}
